package com.allwees.support.marketing.aop;

import cn.hutool.core.util.ReflectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.allwees.bs.core.model.product.entity.ProductModel;
import com.allwees.bs.core.model.util.PriceShouldAdjusted;
import com.allwees.bs.core.modelbase.page.PaginateDTO;
import com.allwees.bs.core.modelbase.vo.R;
import com.allwees.support.marketing.handler.PriceAdjustor;
import org.apache.commons.collections4.CollectionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.Collection;
import java.util.List;

/**
 * 加价功能独立出来，A/B端和C端均可重用
 * 采用AOP的方式进行加价的原因如下：
 * 1.商品搜索不是通过搜索引擎，且有缓存，如果加价后的价格存放在数据库中，要考虑缓存一致性的问题；
 * 2.商品会有编辑、改价等操作，完成后也要刷新缓存，这个过程是比较繁琐和耗时的，可能会出现用户看到的价格和结算的价格不一致的问题；
 * 综上，采用这种拦截的方式，对特定的查询商品的API进行拦截，根据最新的加价规则(如果A端编辑/启用/禁用加价规则，保存成功后会通过异步消息广播，
 * 让各个进程更新本地内存中的规则数据)对其进行加价，以保证加价的实时性和准确性
 * @Author Daniel
 * @Version 1.0.0
 * @Description
 * @CreateTime 2020/9/2 17:07
 */
@Component
@Aspect
public class ResetPriceAspect {

    @Value("${system.params.prod.freePostTag:Free Shipping!}")
    private String freePostTag;

//    @Value("${system.params.prod.almotGoneTag:Almost Gone!}")
//    private String almotGoneTag;


    @Pointcut("@annotation(com.allwees.support.marketing.aop.ResetPrice)")
    public void pointcut() {}

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object result = pjp.proceed();
        if (result instanceof R) {
            R r = (R) result;
            process(r.get("data"));
        } else {
           process(result);
        }
        return result;
    }

    private void process(Object data) {
        if (data instanceof Page<?>) {
            processPage(((Page<?>) data));
        } else if (data instanceof List) {
            processList((List<?>) data);
        } else if (data instanceof PaginateDTO) {
            processList(((PaginateDTO) data).getResults());
        } else {
            doProcess(data);
        }
    }

    private <T> void processPage(Page<T> page) {
        processList(page.getRecords());
    }

    private <T> void processList(List<T> records) {
        if(records == null){
            return;
        }
        for (Object obj : records) {
            doProcess(obj);
        }
    }


    private static final String SPECIAL_PROCESS_CLASS_1 = "com.allwees.bs.c.module.product.dto.ProductDetailsDTO";
    private static final String SPECIAL_PROCESS_CLASS_2 = "com.allwees.bs.c.module.user.dto.ProductCollectionDto";

    /**
     * 1.列表商品没有关联sku，直接处理；
     * 2.商品详情中关联了sku列表，也需要处理；
     * 3.收藏的商品也要处理；
     * @param obj
     */
    private void doProcess(Object obj) {
        if (obj == null) {
            return;
        }
        String className = obj.getClass().getName();
        if (obj instanceof PriceShouldAdjusted){
            PriceAdjustor.adjustPrice((PriceShouldAdjusted)obj);
            if (obj instanceof ProductModel) {
                ProductModel p = (ProductModel) obj;
                //if (p.isFlashProduct()) {
                    setTags(p);
                //}
            }

            //商品详情中的sku处理
            if(SPECIAL_PROCESS_CLASS_1.equals(className)){
                Object skuWrapper = ReflectUtil.getFieldValue(obj, "skus");
                Object skus = ReflectUtil.getFieldValue(skuWrapper, "skus");
                if(skus instanceof Collection){
                    Collection<Object> list = (Collection) skus;
                    if(CollectionUtils.isNotEmpty(list)){
                        list.forEach(this::doProcess);
                    }
                }
            }
        } else if (SPECIAL_PROCESS_CLASS_2.equals(className)){
            this.doProcess(ReflectUtil.getFieldValue(obj, "productDto"));
        }
    }

    private static final DecimalFormat DF = new DecimalFormat("0%");

    /**
     * DecimalFormat.format 非线程安全
     * @param product
     */
    private synchronized void setTags(ProductModel product){
        BigDecimal priceOrigin = product.getPriceOrigin();
        BigDecimal priceRetail = product.getPriceRetail();
        if(BigDecimal.ZERO.compareTo(priceOrigin) != 0 && priceRetail.compareTo(priceOrigin) != 0){
            BigDecimal discount = priceRetail.subtract(priceOrigin).divide(priceOrigin, 2, BigDecimal.ROUND_HALF_UP);
            product.setDiscountOff(DF.format(discount.doubleValue()));
        }

        BigDecimal priceShip = product.getPriceShip();
        if(BigDecimal.ZERO.compareTo(priceShip) == 0){
            product.setFreePost(freePostTag);
        }
    }
}
